local skynet = require "skynet"
local websocket = require"websocket"
local json = require "cjson"
local dump_T = require "dump_t"
local httpc = require "http.httpc"

local pb = require "protobuf"
pb.register_file "./pbc/gctols.pb" 

local CMD = {}
local SOCKET = {}
local gate
local agent = {}
local fd_uid = {}
local uid_agent = {}
local uid_fd = {}
skynet.register_protocol{
	name = "client",
	id = skynet.PTYPE_CLIENT,
	unpack = skynet.tostring
}

local function send_msg(fd,msg)
	websocket:send_binary(fd,msg)
end

function SOCKET.open(fd, addr)
	skynet.error("New client from : " .. addr)
	skynet.call(gate,"lua","accept",fd)
end

local function close_agent(fd)
	local uid = fd_uid[fd]
	if uid then
		-- 发送 顶号
		local respRemoteLogin = {
			uid = uid;
		}
		local bytes = assert(pb.encode("GCToLS.RespRemoteLogin", respRemoteLogin))
		local RespRemoteLogin = {
			msgid = "eMsg_RemoteLogin",
			data = bytes,
		}
		local respData = assert(pb.encode("GCToLS.ReqXieYi", RespRemoteLogin))
		send_msg(fd, respData);
	
		fd_uid[fd]=nil
		local a = uid_agent[uid]
		uid_agent[uid]=nil
		uid_fd[uid] = nil
		skynet.call(gate, "lua", "kick", fd)
		-- disconnect never return
		skynet.send(a, "lua", "disconnect")
	end
end

function SOCKET.close(fd)
	skynet.error("socket close",fd)
	close_agent(fd)
end

function SOCKET.error(fd, msg)
	skynet.error("socket error",fd, msg)
	close_agent(fd)
end

function SOCKET.warning(fd, size)
	-- size K bytes havn't send out in fd
	skynet.error("socket warning", fd, size)
end

local function http_login(code)
	local recvheader = {
		
	}
	local header = {
		
	}
	local content = {
		
	}
	local appid = "wxcd1cc3346958045b"
	local secret = "51731047d33ace59b98ab75da1267976"
	local js_code = code or ""
	local grant_type = "authorization_code"
		
	httpc.timeout = 100	
	protocol = protocol or "http"
	local host = "https://api.weixin.qq.com"
	local url = host .. "/sns/jscode2session" .. string.format("?appid=%s&secret=%s&js_code=%s&grant_type=%s", appid, secret, js_code, grant_type)
	
	--method, hostname, url, recvheader, header, content
	local status, body = httpc.get(host, url, recvheader,header,content)
	print("[body] =====>", status)
	print(body)
	return body
end

---------------------------socket数据处理----------------------------
local sock_handler = {}

sock_handler["eMsg_Login"] = function (fd, rmsg)
	local userinfo =  {};
	local name = "GCToLS.ReqLogin";
	local reqData = assert(pb.decode("GCToLS.ReqLogin",rmsg.data))
	local mysqldb_addr = skynet.uniqueservice"mysqldb"
	local ret = nil
	local respLoginData = {
		askid = reqData.askid,
		userid = 0,
		nflag = 1,
	}
	if reqData.platform == 0 then
		if reqData.uid and reqData.uid ~= "" then
			-- 登录用设备码
			ret = skynet.call(mysqldb_addr,"lua","checkDeviceId",reqData.uid);
			table.print(#ret)
			-- 没有找到玩家，新建玩家
			if ret and #ret == 0 then
				math.randomseed(os.time())
				local userid = math.random(100000,999999);
				local userinfo = {
					userid = userid,
					nickname = "test_" .. userid,
					deviceid = reqData.uid,
				}
				ret = skynet.call(mysqldb_addr,"lua","insertUser",userinfo);
			end
		end
	elseif reqData.platform == 1 then
		-- 采用微信登录
		local result = http_login(reqData.code);
		local data = json.decode(result);
		if data.openid then
			ret = skynet.call(mysqldb_addr,"lua","checkOpenid",data.openid);
			table.print(#ret)
			-- 没有找到玩家，新建玩家
			if ret and #ret == 0 then
				math.randomseed(os.time())
				local userid = math.random(100000,999999);
				local userinfo = {
					userid = userid,
					nickname = "test_" .. userid,
					deviceid = reqData.uid,
					openid = data.openid,
				}
				ret = skynet.call(mysqldb_addr,"lua","insertUserByOpenid",userinfo);
			end
		else
			print("login ack errcode = " .. data.errcode)
			respLoginData.nflag = 2;
			respLoginData.msg = result;
		end
	end
	if ret and #ret > 0 then
		respLoginData.nflag = 0;
		respLoginData.userid = ret[1].userid;
		userinfo = {
			userid = ret[1].userid,
			nickname = ret[1].nickname,
			deviceid = ret[1].deviceid,
			headurl = ret[1].headurl,
		}
	end
	table.print(respLoginData);
	
	local bytes = assert(pb.encode("GCToLS.RespLogin", respLoginData))
	local ReqXieYi = {
		msgid = "eMsg_LoginAck",
		data = bytes,
	}
	local respData = assert(pb.encode("GCToLS.ReqXieYi", ReqXieYi))
	send_msg(fd, respData);
	
	if ret and #ret> 0 and respLoginData.userid ~= 0 then
		local agent = uid_agent[respLoginData.userid]
		local last_fd = uid_fd[respLoginData.userid]
		if agent then
			close_agent(last_fd)
		end
		agent = skynet.newservice("agent")
		fd_uid[fd] = respLoginData.userid
		uid_fd[respLoginData.userid] = fd
		uid_agent[respLoginData.userid] = agent
		skynet.call(agent, "lua", "start", { gate = gate, client = fd, watchdog = skynet.self(),info=userinfo, uid=respLoginData.userid})
	end
end

-- 心跳
sock_handler["eMsg_Heart"] = function (fd, rmsg)
	local reqData = assert(pb.decode("GCToLS.ReqHeart",rmsg.data))
	local respHeartData = {
		askid = reqData.askid,
	}
	local bytes = assert(pb.encode("GCToLS.RespHeart", respHeartData))
	local ReqXieYi = {
		msgid = "eMsg_HeartAck",
		data = bytes,
	}
	local respData = assert(pb.encode("GCToLS.ReqXieYi", ReqXieYi))
	send_msg(fd, respData);
end

--玩家连接上来 消息没有转发给client处理 这里有watchdog处理验证
function SOCKET.data(fd, msg)
	skynet.error("watchdog datasize:",#msg)
	-- 登录处理
	local rmsg = assert(pb.decode("GCToLS.ReqXieYi", msg))
	local respData = {}
	local name = "";
	local reqData = {}
	if rmsg then
		if sock_handler[rmsg.msgid] then
			sock_handler[rmsg.msgid](fd, rmsg);
		else 
			-- 假如不是登录，就要检查是否是登录过的
		end
	end
end

function CMD.start(conf)
	skynet.call(gate, "lua", "open" , conf)
end

function CMD.close(fd)
	close_agent(fd)
end

skynet.start(function()
	skynet.dispatch("lua", function(session, source, cmd, subcmd, ...)
		if cmd == "socket" then
			local f = SOCKET[subcmd]
			f(...)
			-- socket api don't need return
		else
			local f = assert(CMD[cmd])
			skynet.ret(skynet.pack(f(subcmd, ...)))
		end
	end)
	gate = skynet.newservice("wsgate")
end)
